/*
 * LCD.c
 *
 * Optrex LCD Display Code 
 * Created: 4/1/2013 5:23:33 PM
 *  Author: Administrator
 */ 

#include "System.h"

#include <avr/io.h>
#include <util/delay.h>


#include "Pins.h"
#include "LCD.h"


/*------------------------------------------------------------------------
* LCD Command Constants
*
*	See data sheet Section 3
*	Note: Some commands have don't care bits (ie X) or must be logic
*	ORed with arguments
*------------------------------------------------------------------------*/

/**************************************************************************
* Write Commands (ie. RS=0 and R/W =0)
*************************************************************************/

/*----------------------------------------------
* Clear Display (3.1.1)
*		Writes space into all locations
*		Moves the cursor to the top left
*
*	RS=0 and R/W =0
*	0000 0001
*----------------------------------------------*/
#define	LCD_CMD_ClearDisplay 0x1

/*----------------------------------------------
; Return Home (3.1.2)
;		Sets DDRAM Address 0 in address counter
;		Returns display to original position if shifted
;		DDRAM (text on display) remains unchanged
;
;	RS=0 and R/W =0
;	0000 001x
;----------------------------------------------*/
#define	LCD_CMD_ReturnHome		 0x2



/*----------------------------------------------
; Entry Mode Set (3.1.3)
;
;	has 2 x 1 bit arguments which really makes it
;	4 different commands listed below
;
;	RS=0 and R/W =0
;	0000 01 IS
;----------------------------------------------*/
#define	LCD_CMD_EntryModeSet 	 0x04
#define	lcd_IncrementMode 	 0x02		// Increments DDRAM addres when set, decrements when cleared
#define	lcd_ShiftMode 		 0x01		// Shifts display left or right when set (looks like the cursor stays put and the text moves)


#define	LCD_CMD_SetIncrementMode  LCD_CMD_EntryModeSet | lcd_IncrementMode
//		Increments DDRAM Address when character codes are written

#define	LCD_CMD_SetIncrementShiftMode  LCD_CMD_EntryModeSet | lcd_IncrementMode | lcd_ShiftMode
//		Increments DDRAM Address when character codes are written

#define	LCD_CMD_SetDecrementMode  LCD_CMD_EntryModeSet
//		Decrements DDRAM Address when character codes are written


#define	LCD_CMD_SetDecrementShiftMode  LCD_CMD_EntryModeSet | lcd_ShiftMode
//		Decrements DDRAM Address and shifts when character codes are written


/*-----------------------------------ff-----------
; Display ON/OFF Control (3.1.4)
;
;	Controls the Display ON/OFF, Cursor On/Off, and Cursor blink status
;
;	RS=0 and R/W =0
;	0000 1DCB  	where D = display on/off C = cursor on/off B = blink on/off
;----------------------------------------------*/
#define	LCD_CMD_SetDisplayOff		0x08		// turns display off
#define	LCD_CMD_SetDisplayOn		0x0C		// turns display on
#define	lcd_cursor					0x2			// when set the cursor is turned on
#define	lcd_cursor_blink			0x1			// when set the cursor blinks


/*----------------------------------------------
; Cursor or Display Shift (3.1.5)
;	This is actually 4 commands
;
;	RS=0 and R/W =0
;	0001 SDxx
;		where 	S = cursor/display select
;			  	D  = direction select
;
;	Note: the address counter does not change
;---------------------------------------------*/
#define	LCD_CMD_ShiftCursorLeft		= 0x10		// shifts the cursor to the left
#define	LCD_CMD_ShiftCursorRight	= 0x14		// shifts the cursor to the right
#define	LCD_CMD_ShiftDisplayLeft	= 0x18		// shifts the entire display to the left
#define	LCD_CMD_ShiftDisplayRight	= 0x1C		// shifts the entire display to the right


/*----------------------------------------------
; Function Set (3.1.6)
;
;	Sets the interface data length, number of lines
;	and character font
;
;	RS=0 and R/W =0
;	001D NFxx
;		where	D =1 for 8-bit, D =0 for 4 bit interface
;				N =0 for 1 line display , N = 1 for 2 line display
;				F = 1 for 5x10 dots, F=0 for 5x7 dots
;
;----------------------------------------------*/
#define	LCD_CMD_FuntionSet		0x20
#define	lcd_Fuction8Bit			0x10		// specifies 8-bit mode
#define	lcd_Fuction4Bit			0			// specifies 4-bit mode
#define	lcd_FunctionOneLine		0x0			// specifies one line display
#define	lcd_FunctionTwoLine		0x08		// specifies two line display
#define	lcd_Function5x7Dots		0x0			// specifies 5x7 dot characters
#define	lcd_Function5x10Dots	0x04		// specifies 5x10 dot characters


/*----------------------------------------------
; Set CG RAM Address
;
;	Sets the address counter to the CG RAM. Data
;	is then written/read from the CG RAM
;
;	RS=0 and R/W =0
;	01AA AAAA
;
;		where A are the address bits
;----------------------------------------------*/
#define	LCD_CMD_Set_CGRAMAddress		0x40
#define	lcd_cgram_addrmask				0x3F	//masks CGRAM addresses


/*----------------------------------------------
; Set DD RAM Address
;
;	Sets the address counter to the DD RAM. Data
;	is then written/read from the DD RAM
;
;	RS=0 and R/W =0
;	1AAA AAAA
;
;		where A are the address bits
;----------------------------------------------*/
#define	LCD_CMD_Set_DDRAMAddress	 0x80
#define	lcd_ddram_addrmask			 0x7F	// masks DDRAM addresses







/**************************************************************************
; Read Commands (ie. R/W =1)
;*************************************************************************/

/*----------------------------------------------
; Read busy flag and address
;
;	Reads the busy flag and the value of the address counter
;
;	RS=0 and R/W =1
;	BAAAA AAAA
;		where	B = busy flah
;				A are the address bits
;----------------------------------------------*/
#define	lcd_BusyFlag		0x80
#define	lcd_addrmask		0x7F	// masks addresses




#define	LCD_Line2Address	0x40

#define	lcd_lines			2			// number of lines on our LCD
#define	lcd_linelength		20			// number of characters on each LCD line





static void LCD_Write_Cycle(int8_t Data); 
static int8_t LCD_Read_Cycle(void);
static void LCD_Write_Cmd_NoWait(int8_t cmd);
static void LCD_Write_Cmd(int8_t cmd);
static uint8_t LCD_Read_Status(void);
static uint8_t LCD_Read_Data(void);
static void LCD_Write_Data(uint8_t data);
static void LCD_WaitForNotBusy(void);
static void LCD_Set_DDAddress(uint8_t address);



/*
* Configures the I/O pins necessary to communicate with the LCD module
*/
void Initialize_LCD_IO(void)
{
	// Configure LDC R/W pin
	CLEAR_BIT(LDC_RW_PORT, LDC_RW);			// value =0	
	SET_BIT(LDC_RW_DDR, LDC_RW);			// output pin
	
  	

	// Configure the LDC E pin
	CLEAR_BIT(LDC_E_PORT,LDC_E);		// output  = 0
	SET_BIT(LDC_E_DDR, LDC_E); 			// output pin

	// Configure the LDC RS signal
	CLEAR_BIT(LDC_RS_PORT,LDC_RS);	// output  = 0
	SET_BIT(LDC_RS_DDR, LDC_RS); 	// output pin


	// Configure the data bus as inputs (safest state)
	LDC_DATA_DDR =0;		// inputs
	LDC_DATA_PORT =0;	// value = 0x0
	
	
	
	
}



//------------------------------------------------------------------------
// Writes a single command byte to the LCD
//
//	Inputs: 	R = byte to write
//
// See data sheet page 5 for write operation timing
//
//	Notes:
//		1) at 20 MHz each cycle is 50ns
//		2) the minimum cycle time (500 ns) is satisfied by 10 instructions after E=1
//------------------------------------------------------------------------
static void LCD_Write_Cycle(int8_t Data) 
{
	
	CLEAR_BIT(LDC_RW_PORT, LDC_RW);			// R/W =0
	_delay_us(1);									// wait Tas >= 100 ns

	SET_BIT(LDC_E_PORT, LDC_E);			// E = 1
	LDC_DATA_PORT = Data;			// output data

	LDC_DATA_DDR = 0xFF;			// change port to outputs
	_delay_us(1);			// wait Twhe >= 300ns   (6 cycles)
	CLEAR_BIT(LDC_E_PORT,LDC_E);	// E =0
	_delay_us(1);			// wait Tdhw <=10 ns and Tah <=10ns
	LDC_DATA_DDR = 0x00;			// and convert port back to inputs

}



//------------------------------------------------------------------------
// Reads a byte from the LCD
//
//	Outputs: 	R = byte to write
//
// See data sheet page 4 for write operation timing
//
//	Notes:
//		1) at 20 MHz each cycle is 50ns
//		2) the minimum cycle time (500 ns) is satisfied by 10 instructions after E=1
//------------------------------------------------------------------------
static int8_t LCD_Read_Cycle(void)
{
	
	SET_BIT(LDC_RW_PORT,LDC_RW);		// R/W = 1
	
	LDC_DATA_DDR =0x00;					// change port to inputs and wait Tas>=100 ns
	SET_BIT(LDC_E_PORT,LDC_E);			// E = 1
	
	_delay_us(1);						// wait minimum high time Twhe = 300ns

	int8_t rval =  LDC_DATA_PINS; 		// read byte from port
	CLEAR_BIT(LDC_E_PORT,LDC_E);		// E=0

	_delay_us(1);			//wait Tah >=10n
	return rval;
}								


//------------------------------------------------------------------------
// Writes a command to the IR
//
//	Inputs: 	R = command to write
//------------------------------------------------------------------------
static void LCD_Write_Cmd_NoWait(int8_t cmd)
{
	CLEAR_BIT(LDC_RS_PORT,LDC_RS);			// RS = 0
	LCD_Write_Cycle(cmd);					// write a byte
}


static void LCD_Write_Cmd(int8_t cmd)
{
	LCD_WaitForNotBusy();		// wait for busy to clear
	LCD_Write_Cmd_NoWait(cmd);	// write the command
}



//------------------------------------------------------------------------
// Reads the Busy flag and address counter
//
//	Outputs: 	R = status value
//------------------------------------------------------------------------
static uint8_t LCD_Read_Status(void)
{
	CLEAR_BIT(LDC_RS_PORT,LDC_RS);// RS = 0
	return	LCD_Read_Cycle();					// read a byte
}


//------------------------------------------------------------------------
// Reads Data from the CG/DD ram
//
//	Outputs: 	R = status value
//------------------------------------------------------------------------
static uint8_t LCD_Read_Data(void)
{
	LCD_WaitForNotBusy();			// wait for not busy
	SET_BIT(LDC_RS_PORT,LDC_RS);	// RS = 1
	return	LCD_Read_Cycle();		// read a byte
}

//------------------------------------------------------------------------
// Writes a value to the DR
//
//	Inputs: 	R = data value
//------------------------------------------------------------------------
static void LCD_Write_Data(uint8_t data)
{
		LCD_WaitForNotBusy();			// wait for not busy

	SET_BIT(LDC_RS_PORT,LDC_RS);			// RS = 1
	LCD_Write_Cycle(data);					// write a byte
}


void LCD_WriteString(const char *str, 
					uint8_t N)
{
	if ((!str) || (N==0)) return;
	
	while (N>0)
	{
		if (!*str) return;	
		
		LCD_Write_Data(*str);
		str++;
		N--;
	}	
}





//------------------------------------------------------------------------
// Reads the Busy flag and returns when not busy
//
//	Outputs: 	R = busy flag + address
//------------------------------------------------------------------------
static void LCD_WaitForNotBusy(void)
{
	
	
	
	
	uint8_t Count=150;	// count down variable
	
	while ((LCD_Read_Status() & lcd_BusyFlag) && (Count >0))
	{
		_delay_ms(1);	
		Count--;
	}
}

//------------------------------------------------------------------------
// Clears the LCD display and sets the display data address to 0
//
//	Outputs: 	R = busy flag + address
//------------------------------------------------------------------------
 void LCD_ClearDisplay(void)
{
	LCD_Write_Cmd(LCD_CMD_ClearDisplay);
}






//------------------------------------------------------------------------
// Sets the DDRAM address to the specified value
//
//	R = DDRAM Address
//------------------------------------------------------------------------
static void LCD_Set_DDAddress(uint8_t address)
{
	// mask out any high address bits set and insert the command
	LCD_Write_Cmd((address & lcd_ddram_addrmask) | LCD_CMD_Set_DDRAMAddress);
}

//------------------------------------------------------------------------
// Sets the next cursor position to the specified position on the first line
//
//	Argument 1// Line number (0,1)
//	Argument 2: Cursor Position on the line
//------------------------------------------------------------------------
 void LCD_SetPosition(uint8_t Line, uint8_t Position)
{
	LCD_Set_DDAddress((Line*LCD_Line2Address) + Position);
}


//------------------------------------------------------------------------
// Performs all the LCD initialization procedures required for first use
//
//	This is based on Section 2.2.2.1 of the data sheet for 8-bit initialization
//------------------------------------------------------------------------
void Initialize_LCD(void)
{
	

	_delay_ms(16);	

	LCD_Write_Cmd_NoWait(LCD_CMD_FuntionSet | 
						lcd_Fuction8Bit);


	_delay_ms(5);				// wait at least 4.1ms

	LCD_Write_Cmd_NoWait(LCD_CMD_FuntionSet |
						 lcd_Fuction8Bit);


	_delay_ms(1);


	LCD_Write_Cmd_NoWait(LCD_CMD_FuntionSet | 
							lcd_Fuction8Bit);


	LCD_Write_Cmd( LCD_CMD_FuntionSet | 
					lcd_Fuction8Bit | 
					lcd_FunctionTwoLine |
					 lcd_Function5x10Dots); //  0x38			// function set (8 bit etc)
	

	LCD_Write_Cmd(LCD_CMD_SetDisplayOff);

	LCD_Write_Cmd(LCD_CMD_SetIncrementMode);
 		
	LCD_Write_Cmd(LCD_CMD_SetDisplayOn);	// display on

	LCD_Write_Cmd(LCD_CMD_ClearDisplay);

}










